﻿using Engine.Graphics;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace Scenario_Editor.CustomControls
{
    public partial class MaterialListUI : UserControl
    {
        #region Private Fields

        /// <summary>
        /// Control for editing the materail which comes from scenery editor addin.
        /// </summary>
        private AddinManager.IMaterialEditorControl materialEditControl;

        private int materialIDLastSelceted;

        /// <summary>
        /// Reference to the materialmanager which is used in scenery editor.
        /// </summary>
        private MaterialManager materialManager;

        #endregion Private Fields

        #region Public Constructors

        public MaterialListUI()
        {
            InitializeComponent();
            this.listView1.SmallImageList = new ImageList();
        }

        #endregion Public Constructors

        #region Internal Delegates

        internal delegate void MaterialChange(object material, EventArgs e);

        #endregion Internal Delegates

        #region Internal Events

        /// <summary>
        /// When selected Material is changed, an event is fired and the new selected material send.
        /// </summary>
        internal event MaterialChange selectedMaterialChange;

        #endregion Internal Events

        #region Public Methods

        public List<int> GetSelectedMaterials()
        {
            List<int> selectedMaterials = new List<int>();
            for (int i = 0; i < this.listView1.Items.Count; i++)
            {
                if (this.listView1.Items[i].Selected)
                    selectedMaterials.Add(i);
            }
            return selectedMaterials;
        }

        public void SetControlForMaterialEdit(Scenario_Editor.AddinManager.IMaterialEditorControl materialControl)
        {
            if (panelForAddin.Controls.Count > 0)
                panelForAddin.Controls.Clear();

            UserControl control = (UserControl)materialControl;
            this.tableLayoutPanel1.RowStyles[1].Height = control.Height + this.tableLayoutPanel1.Margin.Top + this.tableLayoutPanel1.Margin.Bottom;
            control.Dock = DockStyle.Fill;
            panelForAddin.Controls.Add(control);
            panelForAddin.BackColor = control.BackColor;

            materialEditControl = (Scenario_Editor.AddinManager.IMaterialEditorControl)materialControl;
            materialEditControl.MaterialChanged += MaterialOfControlEdited;
        }

        public void SetMaterialManager(object materialManagerInput, EventArgs e)
        {
            MaterialManager newMaterialManager = (MaterialManager)materialManagerInput;

            if (materialManagerInput != null)
            {
                if (this.materialManager != null)
                    this.materialManager.materialChange -= OnMaterialChange;

                this.materialManager = newMaterialManager;
                this.materialManager.materialChange += new MaterialManager.OnMaterialChange(OnMaterialChange);

                this.listView1.Items.Clear();

                for (int i = 0; i < this.materialManager.GetNumberOfMaterials(); i++)
                {
                    AddMaterial(this.materialManager.GetMaterial(i));
                }
            }
        }

        #endregion Public Methods

        #region Internal Methods

        /// <summary>
        /// this method called by scenery edior loop when the right tabpage is selected.
        /// </summary>
        /// <param name="PositionIn3dRoom"></param>
        /// <param name="engine"></param>
        /// <param name="cameraControl"></param>
        /// <param name="mouseInRenderwindow"></param>
        /// <param name="terrain"></param>
        internal void UseTool(SharpDX.Vector3 PositionIn3dRoom, Engine.Engine engine, Engine.CameraControl cameraControl, bool mouseInRenderwindow, Scenario_Editor.Tools.Terrain.Terrain terrain)
        {
            var mouse = engine.Mouse;
            var keyboard = engine.Keyboard;
            var camera = cameraControl.Camera;
            var cameraTargetPosition = engine.Camera.CameraTartget;

            if (mouseInRenderwindow)
            {
                materialIDLastSelceted = terrain.GetMaterialID(PositionIn3dRoom);
                if (mouse.ButtonL)
                {
                    if (materialIDLastSelceted > -1 && materialIDLastSelceted < this.listView1.Items.Count)
                    {
                        this.listView1.SelectedIndices.Clear();
                        this.listView1.SelectedIndices.Add(materialIDLastSelceted);
                    }
                }
            }

            this.label_MaterialID.Text = (materialIDLastSelceted > -1) ? "MaterialID: " + materialIDLastSelceted.ToString() : "MaterialID: None";

            if (mouseInRenderwindow)
            {
                // Standard camera movement
                if (mouse.ButtonM)// If middle Mouse-Button is pressed
                {
                    engine.Camera.RotateTargetAroundCamera(-mouse.AxisX, -mouse.AxisY);
                }

                if (mouse.AxisZ != 0)// ZOOM in  and out
                {
                    engine.Camera.MoveForward(mouse.AxisZ);
                }

                if (mouse.ButtonR)
                {
                    engine.Camera.MoveUpDown(mouse.AxisY);
                    engine.Camera.MoveXY(-mouse.AxisX, 0.0f);
                }
            }
        }

        #endregion Internal Methods

        #region Private Methods

        /// <summary>
        /// Add a new material to the list. this method creates also the color for the list.
        /// </summary>
        /// <param name="material"></param>
        private void AddMaterial(Engine.Graphics.Material material)
        {
            int index = this.listView1.Items.Count;
            //Create a new Bitmap for the Icon which is shown in the listview-control
            Bitmap bitmap = new Bitmap(1, 1);
            //Create a random Color
            System.Drawing.Color materialColor = System.Drawing.Color.FromArgb((int)(material.MaterialColor.Alpha * 255.0f),
                (int)(material.MaterialColor.Red * 255.0f), (int)(material.MaterialColor.Green * 255.0f), (int)(material.MaterialColor.Blue * 255.0f));

            //fill the bitmap with the random color
            bitmap.SetPixel(0, 0, materialColor);

            Image.GetThumbnailImageAbort myCallback = new Image.GetThumbnailImageAbort(ThumbnailCallback);

            //Add the Icon to the ImageList and resize it to 16x16 Pixels
            int imageListIndex = this.listView1.SmallImageList.Images.Count;
            this.listView1.SmallImageList.Images.Add(bitmap.GetThumbnailImage(16, 16, myCallback, IntPtr.Zero));

            //Add an Item to the listview-control
            ListViewMaterial item = new ListViewMaterial((index + 10000), material, imageListIndex);
            item.SubItems.Add(item.Material.MaterialName);
            this.listView1.Items.Add(item);
        }

        private void button_UseMaterialInMaterialPainter_Click(object sender, EventArgs e)
        {
            ListView.SelectedListViewItemCollection selecteditems = this.listView1.SelectedItems;
            if (selecteditems.Count > 0)
            {
                for (int i = 0; i < 1; i++)
                {
                    int selectedItem = selecteditems[i].Index;
                    if (selectedMaterialChange != null)
                        selectedMaterialChange(this.materialManager.GetMaterial(selectedItem), e);
                }
            }
        }

        private void ChangeMaterial(Engine.Graphics.Material material, MaterialChangeEventArgs e)
        {
            int index = e.index;
            if (index != -1)
            {
                index += 10000;
                //Create a new Bitmap for the Icon which is shown in the listview-control
                Bitmap bitmap = new Bitmap(1, 1);
                //fill the bitmap with the random color
                bitmap.SetPixel(0, 0, System.Drawing.Color.FromArgb(material.MaterialColor.ToRgba()));

                Image.GetThumbnailImageAbort myCallback = new Image.GetThumbnailImageAbort(ThumbnailCallback);
                //Add the Icon to the ImageList and resize it to 16x16 Pixels
                int imageListIndex = this.listView1.SmallImageList.Images.Count;
                this.listView1.SmallImageList.Images.Add(bitmap.GetThumbnailImage(16, 16, myCallback, IntPtr.Zero));

                // Create a new item for the listview-control.
                ListViewMaterial item = new ListViewMaterial(index, material, imageListIndex);

                // add the material name to the item.
                item.SubItems.Add(material.ToString() + "changed");
                // add the item to the listview-control.
                this.listView1.Items.Insert(index - 10000, item);
                // remove the old material
                this.listView1.Items.RemoveAt(index - 10000 + 1);
            }
        }

        private void listView1_SelectedIndexChanged(object sender, EventArgs e)
        {
            ListView.SelectedListViewItemCollection selecteditems = this.listView1.SelectedItems;
            var selectedIndices = this.listView1.SelectedIndices;

            if (selecteditems.Count == 1)
            {
                ListViewMaterial item = (ListViewMaterial)this.listView1.Items[selecteditems[0].Index];
                try
                {
                    int indexOfMaterial = selectedIndices[0];// int.Parse(item.Text);
                    indexOfMaterial -= 10000;
                    // Set materialname in textbox.
                    this.textBox_MaterialName.Text = item.Material.MaterialName;
                    this.textBox_MaterialName.Enabled = true;
                }
                catch (Exception)
                {
                }

                this.materialEditControl.SetMaterial(item.Material.GameMaterialProperties);

                if (this.checkBox_CopyToMaterialPainter.Checked && selectedMaterialChange != null)
                {
                    selectedMaterialChange(item.Material, e);
                }
            }
            else
            {
                this.textBox_MaterialName.Enabled = false;
                this.textBox_MaterialName.Text = string.Empty;
            }
        }

        private void MaterialOfControlEdited(object materialManagerInput, EventArgs e)
        {
            if (this.listView1.SelectedItems.Count > 0)
            {
                foreach (var item in this.listView1.SelectedIndices)
                {
                    var material = this.materialManager.GetMaterial((int)item);
                    material.GameMaterialProperties = (IMaterial)materialManagerInput;
                }
            }
        }

        private void OnMaterialChange(object sender, MaterialChangeEventArgs e)
        {
            switch (e.action)
            {
                case MaterialChangeEventArgs.EnumAction.add:
                    AddMaterial((Engine.Graphics.Material)sender);
                    break;

                case MaterialChangeEventArgs.EnumAction.changed:
                    ChangeMaterial((Engine.Graphics.Material)sender, e);
                    break;

                case MaterialChangeEventArgs.EnumAction.deleted:
                    break;

                default:
                    throw new NotImplementedException(e.action.ToString());
            }
        }

        private void textBox_MaterialName_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter || e.KeyCode == Keys.Return)
            {
                textBox_MaterialName_Leave(null, null);
            }
        }

        private void textBox_MaterialName_Leave(object sender, EventArgs e)
        {
            // check if the materialName don´t exist at the moment
            foreach (ListViewMaterial item in this.listView1.Items)
            {
                if (item.Material.MaterialName.ToUpper() == this.textBox_MaterialName.Text.ToUpper())
                {
                    return;
                }
            }

            if (this.listView1.SelectedItems.Count == 1)
            {
                ListViewMaterial material = (ListViewMaterial)this.listView1.SelectedItems[0];
                material.Material.MaterialName = this.textBox_MaterialName.Text;
                var subitems = material.SubItems;
                // subitem is the materialname in the 2nd column.
                subitems[1].Text = material.Material.MaterialName;
                listView1.Update();
                this.listView1.Refresh();
            }
        }

        private void textBox_MaterialName_TextChanged(object sender, EventArgs e)
        {
            TextBox textBox = (TextBox)sender;
            // check if the materialName don´t exist at the moment
            foreach (ListViewMaterial item in this.listView1.Items)
            {
                if (item.Selected == false && item.Material.MaterialName.ToUpper() == this.textBox_MaterialName.Text.ToUpper())
                {
                    textBox.BackColor = Color.Red;
                    return;
                }
            }
            textBox.BackColor = TextBox.DefaultBackColor;
        }

        private bool ThumbnailCallback()
        {
            return false;
        }

        #endregion Private Methods

        #region Private Classes

        private class ListViewMaterial : ListViewItem
        {
            #region Public Constructors

            /// <summary>
            /// Creates a new listview item which stores the material.
            /// </summary>
            /// <param name="index"></param>
            /// <param name="material"></param>
            /// <param name="imageIndex"></param>
            public ListViewMaterial(int index, Material material, int imageIndex)
                : base(index.ToString(), imageIndex)
            {
                Material = material;
            }

            #endregion Public Constructors

            #region Public Properties

            public Material Material { get; set; }

            #endregion Public Properties

            #region Public Methods

            public override string ToString()
            {
                return this.Material.MaterialName;
            }

            #endregion Public Methods
        }

        #endregion Private Classes
    }
}